/// <reference path="./textureBase.ts" />

namespace FIREFLYX {
    /**
     * 渲染纹理,能接收渲染输出覆盖当前纹理像素
     */
    export class RenderTexture2D extends TextureBase {
        private static _pool: Pool<RenderTexture2D> = new Pool(RenderTexture2D, 0);
        private static _rtTemporaryMap: Map<string, RenderTexture2D> = new Map();

        private _width: number = 256;
        private _height: number = 256;
        private _paramSetting: TextureParamSetting;
        private _dsParamSetting: Nullable<TextureParamSetting>;
        private _enableColor: boolean;
        private _enableDepth: boolean;
        private _enableStencil: boolean;
        private _depthStencilTexture: Nullable<TextureBase>;
        private _fbo: WebGLFramebuffer;

        /** 纹理像素宽 */
        public get width() { return this._width; }
        /** 纹理像素高 */
        public get height() { return this._height; }
        /** 颜色纹理参数设置 */
        public get paramSetting() { return this._paramSetting; }
        /** 深度与模板纹理参数设置 */
        public get depthStencilParamSetting() { return this._dsParamSetting; }
        /** 启用了颜色 */
        public get isEnableColor() { return this._enableColor; }
        /** 启用了深度 */
        public get isEnableDepth() { return this._enableDepth; }
        /** 启用了模板 */
        public get isEnableStencil() { return this._enableStencil; }
        /** webgl 深度和模板纹理 */
        public get depthStencilTexture() { return this._depthStencilTexture; }
        /** fbo 对象 */
        public get fbo() { return this._fbo; }

        /**
         * 实例化一个RenderTexture2D
         * @param name 资源名
         * @param color 开启颜色
         * @param depth 开启深度
         * @param stencil 开启模板
         */
        constructor(name = "", color = true, depth = false, stencil = false) {
            super(name);
            const gl = App.webgl2;
            //fbo
            this._fbo = gl.createFramebuffer() as WebGLFramebuffer;
            //texture attachment set
            this.setFBTexEnable(color, depth, stencil);
        }

        /** 应用尺寸设置 */
        private applySize() {
            // let w = this._width;
            // let h = this._height;
            const gl = App.webgl2;
            //颜色纹理
            let w = this._enableColor ? this._width : 1;    //size 设成1 减少内存占用？
            let h = this._enableColor ? this._height : 1;
            const colorParam = this._paramSetting;
            if (this._glTextrue && colorParam) {
                gl.bindTexture(gl.TEXTURE_2D, this._glTextrue);
                gl.texImage2D(gl.TEXTURE_2D, 0, colorParam.format, w, h, 0, colorParam.getGLFormat(), colorParam.texelType, null);
            }

            //深度和模板纹理
            const enableSDTex = this._enableDepth || this._enableStencil;
            w = enableSDTex ? this._width : 1;
            h = enableSDTex ? this._height : 1;
            const depthParam = this._dsParamSetting;
            if (this._depthStencilTexture && depthParam) {
                gl.bindTexture(gl.TEXTURE_2D, this._depthStencilTexture.glTextrue);
                gl.texImage2D(gl.TEXTURE_2D, 0, depthParam.format, w, h, 0, depthParam.getGLFormat(), depthParam.texelType, null);
            }

            //清理纹理绑定
            gl.bindTexture(gl.TEXTURE_2D, null);
        }

        /**
         * 设置FramebufferTexture 类型的开启
         * @param color 开启颜色
         * @param depth 开启深度
         * @param stencil 开启模板
         */
        public setFBTexEnable(color = true, depth = false, stencil = false) {
            if (this.isDisposed) return;

            const colorDirty = this._enableColor != color;
            const dsDirty = this._enableDepth != depth || this._enableStencil != stencil;
            if (!colorDirty && !dsDirty) return;

            const gl = App.webgl2;
            const fbo = this._fbo;
            if (!fbo) return;

            const lastDepth = this._enableDepth;
            const lastStencil = this._enableStencil;
            this._enableColor = color;
            this._enableDepth = depth;
            this._enableStencil = stencil;
            const enableDS = depth || stencil;  //开启 Stencil Depth
            let needApply = false;

            gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);

            //color set ATTACHMENT
            if (colorDirty) {
                if (!this._glTextrue) {
                    this._glTextrue = gl.createTexture() as WebGLTexture;
                    this._paramSetting = new TextureParamSetting();
                    needApply = true;
                }
                const glColorTex = color ? this._glTextrue : null;
                gl.bindTexture(gl.TEXTURE_2D, glColorTex);
                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glColorTex, 0);
            }

            //depth stencil set ATTACHMENT
            if (dsDirty) {
                if (enableDS) {
                    if (!this._depthStencilTexture) {
                        this._depthStencilTexture = new TextureBase(`depthStencil`);
                        (this._depthStencilTexture as any)._glTextrue = gl.createTexture() as WebGLTexture;
                        this._dsParamSetting = new TextureParamSetting(gl.FLOAT, TextureFormat.DEPTH_COMPONENT32F, false, false, false, TexWrapMode.CLAMP, TexWrapMode.CLAMP);
                        needApply = true;
                    }
                }

                const dsTex = this._depthStencilTexture;
                const dsParamSetting = this._dsParamSetting;

                if (dsTex && dsParamSetting) {
                    const _glDSTex = enableDS ? dsTex.glTextrue : null;
                    const s = enableDS ? stencil : lastStencil;
                    const d = enableDS ? depth : lastDepth;
                    gl.bindTexture(gl.TEXTURE_2D, _glDSTex);
                    let format: TextureFormat;
                    let texelType: GLenum;
                    if (s && d) {
                        texelType = gl.FLOAT_32_UNSIGNED_INT_24_8_REV;
                        format = TextureFormat.DEPTH32F_STENCIL8;
                        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, _glDSTex, 0);
                    } else if (d) {
                        texelType = gl.FLOAT;
                        format = TextureFormat.DEPTH_COMPONENT32F;
                        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, _glDSTex, 0);
                    } else {
                        texelType = gl.UNSIGNED_BYTE;
                        format = TextureFormat.STENCIL_INDEX8;
                        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.TEXTURE_2D, _glDSTex, 0);
                    }

                    if (enableDS && dsParamSetting.format != format || dsParamSetting.texelType != texelType) {
                        dsParamSetting.format = format;
                        dsParamSetting.texelType = texelType;
                        needApply = true;
                    }

                }
            }

            needApply = true;

            //applay
            if (needApply) {
                this.apply();
            }

            this.applySize();
            //检查 FramebufferStatus 
            const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
            if (status !== gl.FRAMEBUFFER_COMPLETE) {
                console.error(`Framebuffer is incomplete: status = ${status}`);
            }

            //set null
            gl.bindTexture(gl.TEXTURE_2D, null);
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        }

        /**
         * 获取临时 renderTextrue2D 对象
         * @returns renderTextrue2D 对象
         */
        public static GetTemporary() {
            let rt = this._pool.new() as RenderTexture2D;
            this._rtTemporaryMap.set(rt.UUID, rt);
            return rt;
        }

        /**
         * 释放临时RenderTexture2D 对象
         * @param rt RenderTexture2D对象
         */
        public static ReleaseTemporary(rt: RenderTexture2D) {
            if (!rt) return;
            this._pool.delete(rt);
            this._rtTemporaryMap.delete(rt.UUID);
        }

        /**
         * 释放所有 临时RenderTexture2D 对象
         */
        public static ReleaseTemporaryAll() {
            this._rtTemporaryMap.forEach((v) => {
                if (v) this._pool.delete(v);
            });

            this._rtTemporaryMap.clear();
        }

        public apply() {
            let gl = App.webgl2;
            if (this._enableColor) {
                TextureParamSetting.applyTex(gl.TEXTURE_2D, this._glTextrue, this._paramSetting);
            }
            if (this._depthStencilTexture && this._dsParamSetting) {
                TextureParamSetting.applyTex(gl.TEXTURE_2D, this._depthStencilTexture.glTextrue, this._dsParamSetting);
            }
        }

        public dispose(): void {
            super.dispose();
            //
            if (this._depthStencilTexture) {
                this._depthStencilTexture.dispose();
                this._depthStencilTexture = null;
            }

            if (this._fbo) {
                let gl = App.webgl2;
                gl.deleteFramebuffer(this._fbo);
                this._fbo = null as any;
            }

            this._dsParamSetting = null;
            this._paramSetting = null as any;
        }
        /**
         * 设置纹理尺寸
         * @param w 纹理宽度
         * @param h 纹理高度
         */
        public setSize(w: number, h: number) {
            if (this._width == w && this._height == h) return;
            this._width = w;
            this._height = h;
            this.applySize();
        }
    }
}